#include "rendering/SceneNode.h"
#include "util/Shared_ptr.h"
#include "component/Transform.h"
#include "component/Renderer.h"
#include "GameObject.h"
#include "Matrix44.h"



using namespace Boon;
using namespace Rendering;


Boon::Rendering::SceneNode::SceneNode( GameObjectID id, const std::string& name, RenderPass rp, ISceneNode* parent )
	: m_Parent( parent )
{
	m_Properties.m_GameObjectID = id;
	m_Properties.m_Name = name;
	m_Properties.m_RenderPass = rp;
}

Boon::Rendering::SceneNode::SceneNode( GameObjectWeak go, ISceneNode* parent )
	:	m_Parent( parent )
{
	if( go.Lock()->GetRenderer() )
		Init( *go.Lock()->GetRenderer() );
}

Boon::Rendering::SceneNode::SceneNode()
	:	m_Parent( nullptr )
{

}

void Boon::Rendering::SceneNode::Init( Renderer& renderer )
{
	m_Properties.m_GameObject = GameObject::GetByID( renderer.GetGameObject().GetUniqueObjectID() );
	GameObject& g = renderer.GetGameObject();
	m_Properties.m_Name = m_Properties.m_GameObject.Lock()->GetName();

	m_Properties.m_RenderPass = renderer.GetRenderPass();
	m_Properties.m_Material = &renderer.GetMaterial();

	m_Properties.m_GameObjectID = g.GetUniqueObjectID();
	SetTransform( g.GetTransform().GetMatrixToWorld(), g.GetTransform().GetMatrixFromWorld() );
}

const SceneNodeProperties* const Boon::Rendering::SceneNode::Get() const
{
	return &m_Properties;
}

void Boon::Rendering::SceneNode::SetTransform( const Matrix44& toWorld, const Matrix44& fromWorld )
{
	m_Properties.m_ToWorld = toWorld;
	m_Properties.m_FromWorld = fromWorld;
}

bool Boon::Rendering::SceneNode::IsVisible( Scene* scene ) const
{
	return true;
}

bool Boon::Rendering::SceneNode::AddChild( const ISceneNodeStrong& child )
{
	static_cast<SceneNode*>( child.Get() )->SetParent( this );
	m_Childs.push_back( child );
	return true;
}

ISceneNodeStrong Boon::Rendering::SceneNode::RemoveChild( const GameObjectID& child )
{
	auto iter = m_Childs.begin();
	auto end = m_Childs.end();

	for( ; iter != end; ++iter )
	{
		if( child == (*iter)->Get()->GameObjectID() )
		{
			ISceneNodeStrong removal = *iter;
			static_cast<SceneNode*>( removal.Get() )->SetParent( nullptr );
			m_Childs.erase( iter );
			return removal;
		}
	}
	return ISceneNodeStrong();
}

bool Boon::Rendering::SceneNode::Update()
{
	auto iter = m_Childs.begin();
	auto end = m_Childs.end();

	for( ; iter != end; ++iter )
		(*iter)->Update();
	return true;
}

bool Boon::Rendering::SceneNode::PreRender( Scene* scene )
{
	NotifyObserver( &IObserverRendering::OnPrerender );
	return true;
}

bool Boon::Rendering::SceneNode::Render( Scene* scene )
{
	NotifyObserver( &IObserverRendering::OnRender );
	return true;
}

bool Boon::Rendering::SceneNode::RenderChildren( Scene* scene )
{
	auto iter = m_Childs.begin();
	auto end = m_Childs.end();

	for( ; iter != end; ++iter )
	{
		// process pre-rendering
		SceneNode* sn = static_cast<SceneNode*>( (*iter).Get() );
		if( sn->PreRender( scene ) )
		{
			if( sn->IsVisible( scene ) )
			{
				//valid pre-rendered; process main renering + childs
				sn->Render( scene );
			}
			sn->RenderChildren( scene );
		}
		sn->PostRender( scene );
	}
	return true;
}

bool Boon::Rendering::SceneNode::PostRender( Scene* scene )
{
	NotifyObserver( &IObserverRendering::OnPostRender );
	return true;
}

void Boon::Rendering::SceneNode::OnRestore()
{
	throw std::exception("The method or operation is not implemented.");
}

void Boon::Rendering::SceneNode::OnLostDevice()
{
	throw std::exception("The method or operation is not implemented.");
}

ISceneNode* Boon::Rendering::SceneNode::GetParent() const
{
	return m_Parent;
}

void Boon::Rendering::SceneNode::SetParent( ISceneNode* parent )
{
	m_Parent = parent;
}

Boon::Rendering::SceneNode::VecSceneNodes& Boon::Rendering::SceneNode::GetChilds()
{
	return m_Childs;
}

Boon::Rendering::SceneNode::~SceneNode()
{
	std::cout << "destroy scene node" << std::endl;

}

Boon::uint_32 Boon::Rendering::SceneNode::RegisterObserver( IObserverRendering* observer )
{
	std::size_t id = reinterpret_cast<std::size_t>( observer );

	auto result = m_Observer.find( id );
	if( result == m_Observer.end() )
	{
		m_Observer[id] = observer;
		return id;
	}
	return INVALID_OBSERVER_ID;
}

bool Boon::Rendering::SceneNode::RemoveObserver( uint_32 observerID )
{
	auto result = m_Observer.find( observerID );
	if( result != m_Observer.end() )
	{
		m_Observer.erase( result );
		return true;
	}
	return false;
}

void Boon::Rendering::SceneNode::NotifyObserver( void (IObserverRendering::*observerCallback)( const InfoObserverRendering&  ) )
{
	auto iter = m_Observer.begin();
	auto end = m_Observer.end();

	while( iter != end )
	{
		// call of onpre/onpos/onrender function()
		InfoObserverRendering info;
		if( !m_Properties.GameObject().Empty() )
		{
			GameObject* g = m_Properties.GameObject().Lock().Get();
			info = InfoObserverRendering (	m_Properties.GameObject(),
											g->GetComponentBase<Collider>(),
											&g->GetTransform().GetMatrixToWorld() );
		}

		(iter->second->*observerCallback)( info );
		++iter;
	}
}

